package finestra;

import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.concurrent.Semaphore;

/**
 * User classes must call {@link #hasNext()} before every {@link #next()},
 * or synchronization doesn't work.<br/>
 * Subclasses must implements {@link #go()}, that will call a sequence
 * of 0 or more {@link #setNext(Object)} followed by one {@link #stop()} 
 * 
 * @author Fernando Tabacchi, fernandotabacchi.com
 *
 * @param <T>
 */
public abstract class LoopInverter<T> implements Iterator<T>,Runnable {
	
	protected T next;
	protected boolean hasNext;
	
	private Semaphore semaphoreLoop;
	private Semaphore semaphoreClient;
	
	
	@Override
	public void remove(){
		throw new UnsupportedOperationException();
	}

	public LoopInverter(){
		
		this.semaphoreClient=new Semaphore(0);
		this.semaphoreLoop= new Semaphore(1);
		this.hasNext=true;
		
		Thread t=new Thread(this);
		t.start();
		
	}
	
	public abstract void go();
	
	
	protected final void setNext(T next){
		try{
			this.semaphoreLoop.acquire();
			this.next=next;
			this.semaphoreClient.release();
		}
		catch(InterruptedException ecc){
			ecc.printStackTrace();
			throw new RuntimeException(ecc.getMessage());
		}
	}
	
	protected final void stop(){
		try{
			this.semaphoreLoop.acquire();
			this.hasNext=false;
			this.semaphoreClient.release();
		}
		catch(InterruptedException ecc){
			ecc.printStackTrace();
			throw new RuntimeException(ecc.getMessage());
		}
	}
	
	
	@Override
	public final boolean hasNext() {
		try{
			this.semaphoreClient.acquire();
			return this.hasNext;
		}
		catch(InterruptedException ecc){
			ecc.printStackTrace();
			throw new RuntimeException(ecc.getMessage());
		}
	}

	@Override
	public final T next() {
		if(!this.hasNext)
			throw new NoSuchElementException();

		T x=this.next;
		this.semaphoreLoop.release();
		return x;
	}
	
	
	
	
	@Override
	public final void run() {
		// other control?
		this.go();
	}
	

}
